/**
 * <p>文件名:	MemcacheManager.java</p>
 * <p>版权:		CopyrightTag</p>
 * <p>公司:		CompanyTag</p>
 * @author		周华彬(zhouhuabin@ctfo.com, zhou_hua_bin@163.com)
 */
package com.caits.lbs.framework.services.memcache;

/**
 * <p>MemcacheManager</p>
 * <p>TODO</p>
 *
 * @author		周华彬(zhouhuabin@ctfo.com, zhou_hua_bin@163.com)
 * @author 		宋晓达 ٩(·̮̃·̃)۶ (songxiaoda@ctfo.com)
 * @version		1.0.0
 * <table style="border:1px solid gray;">
 * <tr>
 * <th width="100px">版本号</th><th width="100px">动作</th><th width="100px">修改人</th><th width="100px">修改时间</th>
 * </tr>
 * <!-- 以 Table 方式书写修改历史 -->
 * <tr>
 * <td>0.0.0</td><td>创建类</td><td>zhb</td><td>2010-11-30 下午03:56:48</td>
 * </tr>
 * <tr>
 * <td>1.0.0</td><td>类改造, 可通过外部对服务器地址进行配置</td><td>sxd</td><td>2011-01-06 11:43</td>
 * </tr>
 * </table>
 */
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeoutException;

import net.rubyeye.xmemcached.exception.MemcachedException;

import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
import redis.clients.util.SafeEncoder;

import com.caits.lbs.exception.LBSException;
import com.caits.lbs.framework.Framework;
import com.caits.lbs.framework.log.CommonLogFactory;
import com.caits.lbs.framework.services.jmx.JMXManager;
import com.caits.lbs.framework.services.sms.IConsumeMessage;
import com.caits.lbs.framework.services.sms.IMessageService;
import com.caits.lbs.framework.services.sms.MessageSMS;
import com.caits.lbs.framework.sms.MockConsumeMessage;
import com.caits.lbs.framework.utils.ObjectSizeUtils;
import com.caits.lbs.framework.utils.StringUtils;

/**
 * <p>
 * MemcacheManager
 * </p>
 * <p>
 * 所有需要调用memcache的地方都可以取得该类的实例，设置好serverAddress后就可以使用
 * </p>
 * 
 * @author 周华彬(zhouhuabin@ctfo.com, zhou_hua_bin@163.com)
 * @version 0.0.0
 *          <table style="border:1px solid gray;">
 *          <tr>
 *          <th width="100px">版本号</th>
 *          <th width="100px">动作</th>
 *          <th width="100px">修改人</th>
 *          <th width="100px">修改时间</th>
 *          </tr>
 *          <!-- 以 Table 方式书写修改历史 -->
 *          <tr>
 *          <td>0.0.0</td>
 *          <td>创建类</td>
 *          <td>zhb</td>
 *          <td>2011-3-18 下午05:16:27</td>
 *          </tr>
 *          <tr>
 *          <td>XXX</td>
 *          <td>XXX</td>
 *          <td>XXX</td>
 *          <td>XXX</td>
 *          </tr>
 *          </table>
 */
public class RedisCacheManager implements IMemcacheService {

	/** 日志记录 */
	protected static Logger log = CommonLogFactory.getLog();

	/** 采用单例模式 */
	protected static RedisCacheManager memCachedManager = null;

	/** 创建全局连接池唯一实例 */
	protected static ShardedJedisPool shardPool = null;
	protected static JedisPool pool = null;
	/** redis连接池配置类 */
	private JedisPoolConfig config = null;  
	 
	/** 高速缓存服务地址 */
	private String serverAddress = "";
	
	/** 服务器验证密码 */
	private String password;
	/** 网络超时时间阈值ms */
	private int soTimeout=3*1000;

	/**
	 * 保护型构造方法 
	 */
	protected RedisCacheManager() {
		if(memCachedManager!=null){
			throw new RuntimeException("实例已经初始化，本次构造放弃.");
		}
		memCachedManager=this;
	}

	/**
	 * 初始化方法, 在applicationContext-redis.xml 中 
	 * <code>
	 * <![CDATA[
	 * <bean id="redisCacheServer" class="com.caits.lbs.framework.services.memcache.RedisCacheManager"
	 * init-method="init"\> 
	 * <property name="config" ref="jedisPoolConfig"><\/property>
	 * <property name="serverAddress" value="192.168.2.186:6379,192.168.4.115:6379"><\/property> 
	 * <\/bean>
	 * ]]>
	 * </code>
	 * */
	public void init() {
		if(pool!=null){
			throw new RuntimeException("实例连接池pool已经初始化，放弃本次执行.");
		}
		/** 设置与缓存服务器的连接池 ,启用主备模式*/
		try {
			//
			List<JedisShardInfo> list = new LinkedList<JedisShardInfo>();  
			String[] server_arr=StringUtils.splitToArray(serverAddress, ",", true);
			Assert.isTrue( server_arr.length>0,"方法init的serverAddress参数错误");
			for(String server:server_arr){
				String[]  hostPort=StringUtils.splitToArray(server, ":", true);
				if(hostPort.length>1){
					JedisShardInfo jedisShardInfo = new JedisShardInfo(  
							hostPort[0], Integer.valueOf(hostPort[1]));  
					jedisShardInfo.setPassword(getPassword());
					list.add(jedisShardInfo);
				}
			}
			JedisShardInfo jedisShardInfo = list.get(0);
			config.setMaxWaitMillis(soTimeout);
			shardPool= (shardPool==null)?new ShardedJedisPool(config, list):shardPool;
			pool= (pool==null)?new JedisPool(config,jedisShardInfo.getHost(),jedisShardInfo.getPort()):pool;
			log.info("RedisCacheManager initialization successful!"+serverAddress);
		} catch (Exception e) {
			log.error("初始化RedisCacheManager连接失败:" + e.getLocalizedMessage(), e);
		}
	}

	/**
	 * 添加
	 * */
	public boolean add(String key, int exp, String value)
			throws LBSException {
		return put(key, exp, value);
	}

	public boolean add(String key, int exp, String value, long expiry)
			throws LBSException {
		return put(key, exp, value, expiry);
	}

	/* (non-Javadoc)
	 * @see com.caits.lbs.framework.services.memcache.IMemcacheService#put(java.lang.String, int, java.lang.Object)
	 */
	public boolean put(final String key, int exp,final Object value) throws LBSException {
		if(key==null||value==null){
			throw new LBSException("方法put传入的对象为空");
		}
		ShardedJedis jedis =null;
//		Jedis jedis =null;
		try{
			jedis= shardPool.getResource();
//			jedis= spool.getResource();
			try {
				byte[] bytes=ObjectSizeUtils.serialize(value);
				jedis.set(SafeEncoder.encode(key),bytes);
				if(exp>0)jedis.expire(SafeEncoder.encode(key), exp);
				return true;
			} catch (Exception e) {
				String message = "设置Redis缓存异常,key="+key;
				log.error(message, e);
				throw new LBSException(message, e);
			}
		}finally{
			// 释放对象池  
			if(jedis!=null&&shardPool!=null)shardPool.returnResource(jedis);
//			if(jedis!=null&&pool!=null)pool.returnResource(jedis);
		}

	}

	/* (non-Javadoc)
	 * @see com.caits.lbs.framework.services.memcache.IMemcacheService#put(java.lang.String, int, java.lang.Object, long)
	 */
	public boolean put(String key, int exp, Object value, long expiry)
			throws LBSException {
		return put(key,exp,value);
	}

	/**
	 * 根据指定的关键字获取对象. 
	 * 返回值的类型是对象，不是简单字符串，须经过反序列化
	 * @param key
	 * @return
	 * @throws MemcachedException
	 * @throws InterruptedException
	 * @throws TimeoutException
	 */
	public Object get(String key) throws LBSException {
		if(key==null){
			throw new LBSException("方法get传入的对象为空");
		}
		ShardedJedis jedis =null;
		try{
			jedis= shardPool.getResource();
			try {
				String type=jedis.type(SafeEncoder.encode(key));
				byte[] bytes = jedis.get(SafeEncoder.encode(key));
				Object value =ObjectSizeUtils.unserialize(bytes);
				return value;
			} catch (Exception e) {
				String message = "获取Redis缓存对象异常,key="+key;
				log.error(message, e);
				throw new LBSException(message, e);
			}
		}finally{
			// 释放对象池  
			if(jedis!=null&&shardPool!=null)shardPool.returnResource(jedis);
		}

	}

	/**
	 * 获取变量<code>soTimeout</code>的值
	 * @return 返回的数据类型是<code>int</code>
	 */
	public int getSoTimeout() {
		return soTimeout;
	}

	/**
	 * 设置变量<code> soTimeout</code> 的值
	 * @param soTimeout  <code>soTimeout</code> 参数类型是<code>int</code>
	 */
	public void setSoTimeout(int soTimeout) {
		this.soTimeout = soTimeout;
	}

	public String getServerAddress() {
		return serverAddress;
	}

	public void setServerAddress(String serverAddress) {
		this.serverAddress = serverAddress;
	}

	/**
	 * 获取变量<code>config</code>的值
	 * @return 返回的数据类型是<code>JedisPoolConfig</code>
	 */
	public JedisPoolConfig getConfig() {
		return config;
	}

	/**
	 * 设置变量<code> config</code> 的值
	 * @param config  <code>config</code> 参数类型是<code>JedisPoolConfig</code>
	 */
	public void setConfig(JedisPoolConfig config) {
		this.config = config;
	}

	/**
	 * 获取变量<code>password</code>的值
	 * @return 返回的数据类型是<code>String</code>
	 */
	public String getPassword() {
		return password;
	}

	/**
	 * 设置变量<code> password</code> 的值
	 * @param password  <code>password</code> 参数类型是<code>String</code>
	 */
	public void setPassword(String password) {
		this.password = password;
	}

	/* (non-Javadoc)
	 * @see com.caits.lbs.framework.services.memcache.IMemcacheService#sendMessage(java.lang.String, java.lang.String)
	 */
	@Override
	public void sendMessage(String head, String body) {
		Jedis  jmsSender = null;
		try {
			Assert.notNull(pool, "消息发送对象pool不能为空.");
			Assert.notNull(body, "消息内容msg不能为空.");
			jmsSender = pool.getResource();
			jmsSender.publish(head, body);
			log.info("消息发送成功,head:" + head + ",body:" + body);
		} catch (Exception e) {
			log.error("消息发送失败,head:" + head + ",body:" + body+",msg=" + e.getLocalizedMessage(), e);
		}finally{
			if(jmsSender!=null&&pool!=null)pool.returnResource(jmsSender);
		}
	}

	/**
	 * 直接获取redis的连接实例.
	 * @return
	 */
	public static synchronized ShardedJedis getShardConnection() {
		Assert.notNull(shardPool,"Redis连接池对象不能为空");
		return shardPool.getResource();
	}
	
	/**
	 * 直接获取redis的连接实例.
	 * @return
	 */
	public static synchronized Jedis getConnection() {
		Assert.notNull(pool,"Redis连接池对象不能为空");
		return pool.getResource();
	}
	/**
	 * 释放redis连接 
	 * @param jedis
	 */
	public static void returnResource(ShardedJedis jedis){
		// 释放对象池  
		if(jedis!=null&&shardPool!=null)shardPool.returnResource(jedis);
	}
	/**
	 * 释放redis连接 
	 * @param jedis
	 */
	public static void returnResource(Jedis jedis){
		// 释放对象池  
		if(jedis!=null&&pool!=null)pool.returnResource(jedis);
	}
	/**
	 * 获取本对象实例 
	 * @return
	 */
	public static RedisCacheManager  getInstance() {
		return memCachedManager;
	}
	/**
	 * 添加一个指定的值到缓存中.
	 * 
	 * @param key
	 * @param value
	 * @return
	 * @throws MemcachedException
	 * @throws InterruptedException
	 * @throws TimeoutException
	 */

	public static void main(String[] args) {
		Framework.startApplicationContext("applicationContext.xml","applicationContext-jmxtest.xml","applicationContext-redis.xml");
		ApplicationContext ac = Framework.context;
		IMemcacheService cache = (IMemcacheService) ac.getBean("redisCacheServer");
		boolean ret = false;
		try {
			ret = cache.put("hello", 0, 234);
			ret = cache.put("kane", 0, "宋晓达");
			log.info("ret=cache.set(\"hello\", 0, 234);");
			log.info("set value ret= " + ret);
			
			log.info("get value : " + cache.get("hello"));
			log.info("get value : " + cache.get("kane"));
		} catch (LBSException e) {
			log.error("main error,msg="+e.getLocalizedMessage(), e);
		}
		IMessageService service = (IMessageService) ac.getBean("redisMessage");
		JMXManager jmx = JMXManager.getInstance();
		if(jmx!=null){
			jmx.addObject(service);
			jmx.addObject(cache);
		}
		MessageSMS messgae = new MessageSMS();
		messgae.setHead("publish");
		messgae.setBody("BASE_1100_13534532340_1020");
		service.sendMessage(messgae );
		IConsumeMessage consumer = new MockConsumeMessage();
		service.registerConsumer(consumer);
		service.startListen();
		boolean bRet=true;
		while(bRet){
			try {
				synchronized (cache) {
					cache.wait();
				}
				
			} catch (InterruptedException e) {
				log.error("main error,msg="+e.getLocalizedMessage(), e);
			}
		}
		System.exit(0);
	}
}
